package com.wingsoft.common.utils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.wingsoft.common.core.domain.entity.SysRole;
import com.wingsoft.common.core.domain.entity.SysUser;
import com.wingsoft.common.core.text.Convert;
import com.wingsoft.common.constant.UserConstants;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 描述
 *
 * @author jiangjiao
 * @date 2025-09-11
 */
public class DataScopUtils {
    public static final String DATA_SCOPE_ALL = "1";
    public static final String DATA_SCOPE_CUSTOM = "2";
    public static final String DATA_SCOPE_DEPT = "3";
    public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
    public static final String DATA_SCOPE_SELF = "5";

    /**
     * 自动拼接数据范围条件（通用）
     * @param wrapper    查询条件
     */
    public static <T> void applyScope(MPJLambdaWrapper<T> wrapper,
                                      String deptField,   // 对应部门字段名
                                      String userField) { // 对应用户字段名

        SysUser user = SecurityUtils.getLoginUser().getUser();

        if (user == null || user.isAdmin()) {
            return;
        }

        for (SysRole role : user.getRoles()) {
            if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE)) {
                continue;
            }

            switch (role.getDataScope()) {
                case DATA_SCOPE_ALL:
                    return; // 全部数据，不限制

                case DATA_SCOPE_DEPT:
                    if (user.getDeptId() != null && StringUtils.isNotBlank(deptField)) {
                        wrapper.eq(deptField, user.getDeptId());
                    }
                    break;

                case DATA_SCOPE_SELF:
                    if (user.getUserId() != null && StringUtils.isNotBlank(userField)) {
                        wrapper.eq(userField, user.getUserId());
                    }
                    break;

                case DATA_SCOPE_CUSTOM:
                    // roleId 必须不为 null 且字段名有效
                    if (role.getRoleId() != null && StringUtils.isNotBlank(deptField)) {
                        wrapper.apply(deptField + " IN (SELECT dept_id FROM sys_role_dept WHERE role_id = {0})",
                                role.getRoleId());
                    }
                    break;

                case DATA_SCOPE_DEPT_AND_CHILD:
                    // deptId 必须不为 null 且字段名有效
                    if (user.getDeptId() != null && StringUtils.isNotBlank(deptField)) {
                        wrapper.apply(deptField + " IN (SELECT dept_id FROM sys_dept " +
                                        "WHERE dept_id = {0} OR FIND_IN_SET({0}, ancestors))",
                                user.getDeptId());
                    }
                    break;

                default:
                    break;
            }
        }
    }

    private static <T> SFunction<T, Object> getColumn(Class<T> entityCls, String fieldName) {
        try {
            String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            Method method = entityCls.getMethod(getterName);
            return t -> {
                try {
                    return method.invoke(t);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            };
        } catch (NoSuchMethodException e) {
            throw new RuntimeException("实体类 " + entityCls.getSimpleName() + " 缺少字段 " + fieldName + " 对应的 getter");
        }
    }



    public static <T> void applyScopeOr(MPJLambdaWrapper<T> wrapper,
                                        String deptColumn,
                                        String userColumn) {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        if (user == null || user.isAdmin()) {
            return;
        }

        Set<String> conditions = new HashSet<>();

        List<Consumer<MPJLambdaWrapper<T>>> orConditions = new ArrayList<>();

        for (SysRole role : user.getRoles()) {
            String dataScope = role.getDataScope();
            if (conditions.contains(dataScope) || UserConstants.ROLE_DISABLE.equals(role.getStatus())) {
                continue;
            }

            switch (dataScope) {
                case DATA_SCOPE_ALL:
                    orConditions = new ArrayList<>();
                    return;

                case DATA_SCOPE_CUSTOM:
                    if (deptColumn != null) {
                        orConditions.add(wq -> wq.apply(
                                deptColumn + " IN (SELECT dept_id FROM sys_role_dept WHERE role_id = {0})",
                                role.getRoleId()
                        ));
                    }
                    break;

                case DATA_SCOPE_DEPT:
                    if (deptColumn != null) {
                        orConditions.add(wq -> wq.eq(deptColumn, user.getDeptId()));
                    }
                    break;

                case DATA_SCOPE_DEPT_AND_CHILD:
                    if (deptColumn != null) {
                        orConditions.add(wq -> wq.apply(
                                deptColumn + " IN (SELECT dept_id FROM sys_dept WHERE dept_id = {0} OR FIND_IN_SET({0}, ancestors))",
                                user.getDeptId()
                        ));
                    }
                    break;

                case DATA_SCOPE_SELF:
                    if (userColumn != null) {
                        orConditions.add(wq -> wq.eq(userColumn, user.getUserId()));
                    }
                    break;

                default:
                    break;
            }

            conditions.add(dataScope);
        }

        //最后统一拼接 OR 条件，避免 AND () 报错
        if (!orConditions.isEmpty()) {
            List<Consumer<MPJLambdaWrapper<T>>> finalOrConditions = orConditions;
            wrapper.and(wq -> {
                for (Consumer<MPJLambdaWrapper<T>> cond : finalOrConditions) {
                    wq.or(cond);
                }
            });
        }
    }

}
